package tech.xs.common.framework.init.dict;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import tech.xs.common.framework.domain.annotations.ChildDict;
import tech.xs.common.framework.domain.annotations.ChildDictData;
import tech.xs.common.framework.domain.annotations.Dict;
import tech.xs.common.framework.domain.annotations.DictData;
import tech.xs.common.framework.domain.constant.ModuleNames;
import tech.xs.common.framework.domain.constant.mq.QueueNames;
import tech.xs.common.framework.domain.enums.BaseEnum;
import tech.xs.common.domain.enums.YesNoEnum;
import tech.xs.common.framework.init.InitExecute;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncChildDictBo;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncChildDictDataBo;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncDictBo;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncDictDataBo;
import tech.xs.common.framework.init.dict.domain.exception.InitDictException;
import tech.xs.common.framework.util.MqUtil;
import tech.xs.common.framework.util.SpringPackageUtil;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 初始化系统字典
 * 通过包扫描器将含有Dict注解的枚举通过Mq推送到Sys服务,Sys服务将数据Mq推送过来的数据持久化到数据库中
 */
@Slf4j
@Component
@Order(400)
public class InitSysDictData implements InitExecute {

    @Value("${spring.application.name}")
    private String module;

    @Override
    public void execute() throws Exception {
        log.info("[MQ生产者][字典构建][开始构建]");
        Set<Class<?>> dictClasses = new HashSet<>();
        Collection<String> packageNames = SpringPackageUtil.getPackageNames();
        for (String packageName : packageNames) {
            dictClasses.addAll(ClassUtil.scanPackageByAnnotation(packageName, Dict.class));
        }
        if (CollUtil.isEmpty(dictClasses)) {
            return;
        }
        List<SyncDictBo> currModuleDictList = new ArrayList<>();
        List<SyncDictBo> commonModuleDictList = new ArrayList<>();
        Set<String> names = new HashSet<>();
        for (Class<?> dictClass : dictClasses) {
            SyncDictBo dict = getEnumDict(dictClass);
            if (dict == null) {
                continue;
            }
            if (names.contains(dict.getName())) {
                throw new InitDictException("字典名称重复:" + dictClass.getName());
            }
            names.add(dict.getName());
            if (ModuleNames.COMMON.equals(dict.getModule())) {
                commonModuleDictList.add(dict);
            } else {
                currModuleDictList.add(dict);
            }
        }
        log.info("[MQ生产者][字典构建] commonModuleDictList:{}", JSONObject.toJSONString(commonModuleDictList));
        log.info("[MQ生产者][字典构建] currModuleDictList:{}", JSONObject.toJSONString(currModuleDictList));
        // 对字典分模块发送
        if (CollUtil.isNotEmpty(commonModuleDictList)) {
            MqUtil.send(QueueNames.SYS_SYNC_DICT, commonModuleDictList);
        }
        if (CollUtil.isNotEmpty(currModuleDictList)) {
            MqUtil.send(QueueNames.SYS_SYNC_DICT, currModuleDictList);
        }
        log.info("[MQ生产者][字典构建][发送完毕]");
    }

    @Override
    public boolean isErrorStop() {
        return true;
    }

    private SyncDictBo getEnumDict(Class<?> dictClass) {
        if (!dictClass.isEnum()) {
            return null;
        }
        Dict dictAnnotation = dictClass.getAnnotation(Dict.class);
        if (dictAnnotation == null) {
            return null;
        }
        SyncDictBo dict = new SyncDictBo();

        dict.setModule(module);
        if (StrUtil.isNotBlank(dictAnnotation.module())) {
            dict.setModule(dictAnnotation.module());
        }

        String code = dictAnnotation.code();
        if (StrUtil.isBlank(code)) {
            code = dictClass.getSimpleName();
        }
        dict.setCode(code);

        String name = dictAnnotation.name();
        if (StrUtil.isBlank(name)) {
            name = dictClass.getSimpleName();
        }
        dict.setName(name);

        dict.setValueType(dictAnnotation.valueType());
        dict.setRemark(dictAnnotation.remark());

        List<SyncDictDataBo> values = new ArrayList<>();
        dict.setDatas(values);
        Field[] fields = dictClass.getFields();
        for (Field field : fields) {
            SyncDictDataBo dictData = getEnumFieldDictData(dictClass, field);
            if (dictData == null) {
                continue;
            }
            values.add(dictData);
        }
        setChildDicList(dict, dictClass);
        return dict;
    }

    private void setChildDicList(SyncDictBo dict, Class<?> dictClass) {
        ArrayList<SyncChildDictBo> childDictList = new ArrayList<>();
        dict.setChildDicts(childDictList);
        Dict dictAnnotation = dictClass.getAnnotation(Dict.class);
        ChildDict[] childDictAnnotationList = dictAnnotation.children();
        if (childDictAnnotationList == null || childDictAnnotationList.length <= 0) {
            return;
        }
        Set<String> childDictCode = new HashSet<>();
        for (ChildDict childDictAnnotation : childDictAnnotationList) {
            SyncChildDictBo item = new SyncChildDictBo();
            if (StrUtil.isBlank(childDictAnnotation.code())) {
                throw new InitDictException("子字典code不能为空 class:" + dictClass.getName());
            }
            item.setCode(childDictAnnotation.code());
            String name = childDictAnnotation.name();
            if (StrUtil.isBlank(name)) {
                name = dict.getName();
            }
            item.setName(name);
            item.setRemark(childDictAnnotation.remark());
            childDictList.add(item);
            childDictCode.add(childDictAnnotation.code());
            if (childDictCode.size() != childDictList.size()) {
                throw new InitDictException("子字典code重复 class:" + dictClass.getName() + " code:" + childDictAnnotation.code());
            }
            List<SyncChildDictDataBo> values = new ArrayList<>();
            item.setDatas(values);
            Field[] fields = dictClass.getFields();
            for (Field field : fields) {
                SyncChildDictDataBo childDictData = getEnumFieldChildDictData(item, dictClass, field);
                if (childDictData == null) {
                    continue;
                }
                values.add(childDictData);
            }
        }
    }

    private SyncChildDictDataBo getEnumFieldChildDictData(SyncChildDictBo childDict, Class<?> dictClass, Field field) {
        SyncChildDictDataBo childDictData = new SyncChildDictDataBo();
        DictData dictDataAnnotation = field.getAnnotation(DictData.class);
        ChildDictData[] children = dictDataAnnotation.children();
        if (children == null || children.length <= 0) {
            return null;
        }
        ChildDictData childDictDataAnnotation = null;
        Set<String> childDictCodes = new HashSet<>();
        for (ChildDictData child : children) {
            if (StrUtil.isBlank(child.childCode())) {
                throw new InitDictException("子字典绑定字典code不能为空 class:" + dictClass.getName());
            }
            childDictCodes.add(child.childCode());
            if (childDict.getCode().equals(child.childCode())) {
                childDictDataAnnotation = child;
            }
        }
        if (childDictCodes.size() != children.length) {
            throw new InitDictException("通一个字段不能绑定同一个子字典多次 class:" + dictClass.getName());
        }
        String name = childDictDataAnnotation.name();
        int order = childDictDataAnnotation.order();
        String remark = childDictDataAnnotation.remark();
        if (StrUtil.isBlank(name)) {
            name = dictDataAnnotation.name();
        }
        if (order == 0) {
            order = dictDataAnnotation.order();
        }
        childDictData.setCode(getDictDataCode(dictClass, field));
        childDictData.setName(name);
        childDictData.setOrderValue(order);
        childDictData.setRemark(remark);
        childDictData.setStatus(YesNoEnum.YES);
        return childDictData;
    }

    private SyncDictDataBo getEnumFieldDictData(Class<?> dictClass, Field field) {
        SyncDictDataBo dictData = new SyncDictDataBo();
        DictData dictDataAnnotation = field.getAnnotation(DictData.class);
        String name = field.getName();
        String code = getDictDataCode(dictClass, field);
        YesNoEnum status = YesNoEnum.YES;
        int order = 0;
        if (BaseEnum.class.isAssignableFrom(dictClass)) {
            Object[] enums = dictClass.getEnumConstants();
            for (Object item : enums) {
                Enum baseEnum = (Enum) item;
                if (name.equals(baseEnum.name())) {
                    order = baseEnum.ordinal();
                    break;
                }
            }
        }
        if (dictDataAnnotation != null) {
            if (StrUtil.isNotBlank(dictDataAnnotation.name())) {
                name = dictDataAnnotation.name();
            }
            if (0 != dictDataAnnotation.order()) {
                order = dictDataAnnotation.order();
            }
            status = dictDataAnnotation.status();
            dictData.setRemark(dictDataAnnotation.remark());
        }
        dictData.setCode(code);
        dictData.setName(name);
        dictData.setOrderValue(order);
        dictData.setStatus(status);
        return dictData;
    }

    private String getDictDataCode(Class<?> dictClass, Field field) {
        String code = field.getName();
        String name = field.getName();
        if (BaseEnum.class.isAssignableFrom(dictClass)) {
            Object[] enums = dictClass.getEnumConstants();
            for (Object item : enums) {
                Enum baseEnum = (Enum) item;
                if (name.equals(baseEnum.name())) {
                    code = ((BaseEnum) item).getValue().toString();
                    break;
                }
            }
        }
        DictData dictDataAnnotation = field.getAnnotation(DictData.class);
        if (dictDataAnnotation != null) {
            if (StrUtil.isNotBlank(dictDataAnnotation.code())) {
                code = dictDataAnnotation.code();
            }
        }
        return code;
    }

}
